import datetime
import os
import sys
from typing import *

from utils.strategy.loader import StrategyLoader
from utils.data.loader import BaseLoader
from utils.data.sqlLoader import loader_mapper
from utils.error import PreparationError
from config import DATABASE_TYPE, STRATEGY_LOG, ROOT_DIR
from utils.tools import NullStdout
from utils.dataArrange.dataHandler import Handler
from utils.strategy.conf.BtConf import BtConf
from globalVar import GlobalVar
import pandas as pd


class BaseExecutor:
    """
    """

    def __init__(self, strategy_name: str):
        if strategy_name not in GlobalVar.ALL_STRATEGIES.keys():
            info = f'\'{strategy_name}\'策略不存在，请检查目录：\'{os.path.join(ROOT_DIR, "strategies")}\''
            raise PreparationError(info)
        # 策略名
        self.strategy_name = strategy_name
        # 策略加载器对象
        self.strategy: Optional[StrategyLoader] = None
        # 计算使用的数据
        self.dataframe: Optional[pd.DataFrame] = None
        self.loader: Optional[BaseLoader] = None
        self.period: Optional[int] = None
        # K线合并数
        self.data_batch: int = 1
        self.max_line_count: int = 1
        self.s_time: int = 0
        self.e_time: int = 0

        # 标准输出流，执行策略时替换为文件IO，防止策略内输出打乱框架输出内容
        self.stdout_bak: Optional[TextIO] = None
        # 策略内输出内容重定向到文件内
        self.logfile: Optional[Union[TextIO, NullStdout]] = None
        # 时间-细目
        self.result_data: Dict[int, Dict[str, Any]] = {}
        # 是否为回测
        self.is_backtest: bool = True
        self.backtest_handlers: Dict[str, Handler] = {}
        # 是否是异步多任务
        self.async_type: bool = False
        self.config: Optional[BtConf] = None

    @property
    def symbols(self) -> List[str]:
        return self.strategy.system_conf.get('symbols', [])

    def remove_attrs(self):
        # 策略加载器对象
        self.strategy: Optional[StrategyLoader] = None
        # 计算使用的数据
        self.dataframe: Optional[pd.DataFrame] = None
        self.loader: Optional[BaseLoader] = None
        self.period: Optional[int] = None
        self.data_batch: int = 1
        self.max_line_count: int = 1
        self.s_time: int = 0
        self.e_time: int = 0

        # 标准输出流，执行策略时替换为文件IO，防止策略内输出打乱框架输出内容
        self.stdout_bak: Optional[TextIO] = None
        # 策略内输出内容重定向到文件内
        self.logfile: Optional[Union[TextIO, NullStdout]] = None
        # 时间-细目
        self.result_data: Dict[int, Dict[str, Any]] = {}
        # 是否为回测
        self.is_backtest: bool = True
        self.backtest_handlers: Dict[str, Handler] = {}
        # 是否是异步多任务
        self.async_type: bool = False
        self.config = None

    def initial_attrs(self, conf: BtConf = None):
        """
        初始化属性
        :param conf:
        :return:
        """
        GlobalVar.START_TIME = conf.s_time
        GlobalVar.END_TIME = conf.e_time
        # 加载策略
        self.strategy = StrategyLoader(self.strategy_name, GlobalVar.ALL_STRATEGIES[self.strategy_name])
        # 注入配置
        self.config = conf
        # 策略中最大使用k线数量
        self.max_line_count: int = self.strategy.system_conf.get('historyKlineCount', 99)
        # K线数据周期 只有minute、hour和day
        data_period: str = self.strategy.system_conf.get('Kline', 'hour')
        self.data_batch: int = self.strategy.system_conf.get('KlineBatchSize', 1)
        # 基础数据周期周期
        if data_period == 'hour':
            period = 3600
        elif data_period == 'day':
            period = 86400
        elif data_period == 'minute':
            period = 60
        else:
            raise PreparationError(f'配置K线周期错误')
        self.period = period
        # 数据加载器
        s_time: int = int(conf.s_time.timestamp())
        self.s_time = s_time - s_time % (period * self.data_batch)
        e_time: int = int(conf.e_time.timestamp())
        self.e_time = e_time - e_time % (period * self.data_batch)

        # 备份系统输出流
        self.stdout_bak = sys.stdout

    def draw(self):
        pass

    def run(self, *confs: BtConf) -> None:
        pass

    def __del__(self):
        if self.logfile is not None:
            self.logfile.close()
